pub fn main() {
    // 引用与解引用
    // main1();
    // 不可变引用
    // main2();
    // 可变引用   可变引用同时只能存在一个
    // main3();
    // main4();
    // 可变引用与不可变引用不能同时存在
    // main6();
    // 悬垂引用(Dangling References)
    main8();
}

fn main1() {
    let s = 5;
    let y = &s;
    assert_eq!(5, s);
    assert_eq!(5, *y);
    // 如果尝试编写 assert_eq!(5, y);，则会得到如下编译错误
    // error[E0277]: can't compare `{integer}` with `&{integer}`
    //  --> src/main.rs:6:5
    //   |
    // 6 |     assert_eq!(5, y);
    //   |     ^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &{integer}` // 无法比较整数类型和引用类型
    //   |
    //   = help: the trait `std::cmp::PartialEq<&{integer}>` is not implemented for
    //   `{integer}`
}

fn main2() {
    // s1 的引用作为参数传递给 calculate_length 函数，而不是把 s1 的所有权转移给该函数：
    let s1 = String::from("hello");
    let len = calculate_length(&s1);
    // // 不能修改借用的变量
    // change(&s1);

    println!("字符 '{}' 的长度是： {}.", s1, len);
}

// usize -> 无符号整数：表示非负整数值（0 及正数）
fn calculate_length(s: &String) -> usize {
    // s 是对 String 的引用
    s.len()
} // 这里，s 离开了作用域。但因为它并不拥有引用值的所有权， 所以什么也不会发生

// fn change(some_string: &String) {
//     /*
//     error[E0596]: cannot borrow `*some_string` as mutable, as it is behind a `&` reference
//      --> src/main.rs:8:5
//       |
//     7 | fn change(some_string: &String) {
//       |                        ------- help: consider changing this to be a mutable reference: `&mut String`
//                                ------- 帮助：考虑将该参数类型修改为可变的引用: `&mut String`
//     8 |     some_string.push_str(", world");
//       |     ^^^^^^^^^^^ `some_string` is a `&` reference, so the data it refers to cannot be borrowed as mutable
//                          `some_string`是一个`&`类型的引用，因此它指向的数据无法进行修改
//
//     */
//     some_string.push_str(", world");
// }

fn main3() {
    let mut s = String::from("hello");
    change(&mut s);
    println!("{}", s);
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

fn main4() {
    // let mut s = String::from("hello");
    //
    // // 可变引用同时只能存在一个
    // let s1 = &mut s;
    // let s2 = &mut s;
    // println!("{}, {}", s1, s2);

    /*
    error[E0499]: cannot borrow `s` as mutable more than once at a time 同一时间无法对 `s` 进行两次可变借用
     --> src/main.rs:5:14
      |
    4 |     let r1 = &mut s;
      |              ------ first mutable borrow occurs here 首个可变引用在这里借用
    5 |     let r2 = &mut s;
      |              ^^^^^^ second mutable borrow occurs here 第二个可变引用在这里借用
    6 |
    7 |     println!("{}, {}", r1, r2);
      |                        -- first borrow later used here 第一个借用在这里使用
    */
    /*
    这段代码出错的原因在于，第一个可变借用 r1 必须要持续到最后一次使用的位置 println!，在 r1 创建和最后一次使用之间，我们又尝试创建第二个可变借用 r2。

    对于新手来说，这个特性绝对是一大拦路虎，也是新人们谈之色变的编译器 borrow checker 特性之一，不过各行各业都一样，限制往往是出于安全的考虑，Rust 也一样。

    这种限制的好处就是使 Rust 在编译期就避免数据竞争，数据竞争可由以下行为造成：
        两个或更多的指针同时访问同一数据
        至少有一个指针被用来写入数据
        没有同步数据访问的机制

    数据竞争会导致未定义行为，这种行为很可能超出我们的预期，难以在运行时追踪，并且难以诊断和修复。而 Rust 避免了这种情况的发生，因为它甚至不会编译存在数据竞争的代码！
    */
    // 很多时候，大括号可以帮我们解决一些编译不通过的问题，通过手动限制变量的作用域
    main5();
}

fn main5() {
    let mut s = String::from("hello");

    {
        let r1 = &mut s;
        println!("{}", r1);
    } // r1 在这里离开了作用域，所以我们完全可以创建一个新的可变引用

    let r2 = &mut s;
    println!("{}", r2);
}

fn main6() {
    // let mut s = String::from("hello");
    //
    // let r1 = &s; // 没问题
    // let r2 = &s; // 没问题
    // let r3 = &mut s; // 大问题
    // println!("{}, {} and {}", r1, r2, r3);
    /*
        error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
                // 无法借用可变 `s` 因为它已经被借用了不可变
         --> src/main.rs:6:14
          |
        4 |     let r1 = &s; // 没问题
          |              -- immutable borrow occurs here 不可变借用发生在这里
        5 |     let r2 = &s; // 没问题
        6 |     let r3 = &mut s; // 大问题
          |              ^^^^^^ mutable borrow occurs here 可变借用发生在这里
        7 |
        8 |     println!("{}, {}, and {}", r1, r2, r3);
          |                                -- immutable borrow later used here 不可变借用在这里使用
    */
    /*
        其实这个也很好理解，正在借用不可变引用的用户，肯定不希望他借用的东西，被另外一个人莫名其妙改变了。
        多个不可变借用被允许是因为没有人会去试图修改数据，每个人都只读这一份数据而不做修改，因此不用担心数据被污染。
    */
    // 你必须非常小心的去安排可变、不可变变量的借用，免得无法通过编译
    main7();
}
fn main7() {
    let mut s = String::from("hello");

    let r1 = &s; // 没问题
    let r2 = &s;
    println!("{} and {}", r1, r2); // 新编译器中，r1,r2作用域在这里结束

    let r3 = &mut s; // 没问题
    println!("{}", r3);
} // 老编译器中，r1、r2、r3作用域在这里结束；新编译器中，r3作用域在这里结束

fn main8() {
    /*
        悬垂引用也叫做悬垂指针，意思为指针指向某个值后，这个值被释放掉了，而指针仍然存在，其指向的内存可能不存在任何值或已被其它变量重新使用。
        在 Rust 中编译器可以确保引用永远也不会变成悬垂状态：
        当你获取数据的引用后，编译器可以确保数据不会在引用结束前被释放，要想释放数据，必须先停止其引用的使用。
    */
    /*
        因为 s 是在 dangle 函数内创建的，当 dangle 的代码执行完毕后，s 将被释放，但是此时我们又尝试去返回它的引用。这意味着这个引用会指向一个无效的 String，这可不对！
        其中一个很好的解决方法是直接返回 String：
    */
    let reference_to_nothing = dangle();
}

// fn dangle() -> &String {  // dangle 返回一个字符串的引用
//     let s = String::from("hello");  // s 是一个新字符串
//
//     &s  // 返回字符串 s 的引用
// }  // 这里 s 离开作用域并被丢弃。其内存被释放。

fn dangle() -> String {
    let s = String::from("hello");

    s
}
